#include "Strategy.hh"
#include "Order.h"
#include "Bfs.hh"

//
// Walking methods
//

/// Neigbouring area Positions Counter Functor
struct balance_counter_t
{
    signed myteam;
    unsigned enemies;
    unsigned friends;
    unsigned invisible;
    unsigned total;


    balance_counter_t(unsigned _myteam) :
        myteam(_myteam), enemies(0), friends(0),
        invisible(0), total(0)
    {}

    bool operator()(Map & map, const bfs_node & node)
    {
        uLocation cLoc = node.cell;

        if(map[cLoc].ant != -1 and map[cLoc].ant != myteam)
            ++enemies;
        else if(map[cLoc].ant != -1 and map[cLoc].ant == myteam)
            ++friends;
        else if(!map[cLoc].isVisible)
            ++invisible;
        ++total;
        return true;
    }
};



/// Linear potential
struct update_value_lin_t
{
    double w;
    unsigned mindepth;

    update_value_lin_t(double _w, unsigned _min = 1) : w(_w), mindepth(_min) {}

    bool operator()(Map & map, const bfs_node & node)
    {
        uLocation cLoc = node.cell;
        unsigned depth = node.depth;

        if(depth >= mindepth)
            map[cLoc].value += w / (1.0 + depth - mindepth);
        return true;
    }
};


void
set_potentials(State & state)
{
  bug << "set null potential" << std::endl;
  for(uLocation cell=0;cell!=state.grid().size();++cell)
    state.grid().at(cell).value = 0;


  bug << "estimate safety levels" << std::endl;
    // estimate rough safety level for path-finding
    estimate_rough_safety(state.grid());

#if 0
    // update cells visibility history
    bug << "set visibility value" << std::endl;
    for(uLocation cell=0;cell!=state.grid().size();++cell)
    {
        if(state.grid().at(cell).isVisible)
            state.grid().at(cell).recently_viewed = 1.0;
        else
	  state.grid().at(cell).recently_viewed *= .9; // slowly forget
    }

    bug << "estimate exploration values" << std::endl;
    for(unsigned cell=0;cell!=state.grid().size();++cell)
    {
        if(not  state.grid().at(cell).isWater and state.grid().at(cell).isVisible)
        {
            double sum=.0;
            double nb=.0;
            for(std::vector<uLocation>::const_iterator it=state.grid().view_reach_map[cell].begin();
                it!=state.grid().view_reach_map[cell].end(); ++it)
            {
                sum += 1.0 - state.grid().at(*it).recently_viewed;
                nb += 1.0;
            }
            state.grid().at(cell).value = 0.5 * explorationw * sum / nb;
        }
    }

#endif

    bug << "estimate exploration values" << std::endl;
    
    uLocation sLoc;
    do
      {
	sLoc = rand()%state.grid().size();
      }
    while(not state.grid()[sLoc].isWater);
    bug << "Exploration target at " << state.grid().get_row(sLoc)
	<< "," << state.grid().get_col(sLoc) << std::endl;
    update_value_lin_t update_explo(explorationw,0);
    walk(state.grid(),sLoc,state.grid().size(),update_explo);

    // update cells centrality
    /*
    bug << "set centrality value of cells" << std::endl;
    double max_reach_surface = state.grid().view_reach_map.front().size();
    for(unsigned cell=0;cell!=state.grid().size();++cell)
    {
      if(not state.grid().at(cell).isWater and state.grid().at(cell).isVisible)
        {
	  balance_counter_t balance_count(0);
	  walk(state.grid(), cell, state.attackradius+1, balance_count);
	  state.grid().at(cell).value += 0.01 * explorationw * (static_cast<double>(balance_count.total)/max_reach_surface);
	}
    }
    */


    bug << "Food areas" <<  std::endl;
    update_value_lin_t update_foods(0.1*foodw,0);
    for(unsigned i=0; i!=state.food.size(); ++i)
      safe_walk(state.grid(),state.food[i],state.viewradius*10,update_foods);


    bug << "Enemy hills" <<  std::endl;
    // enemy hills
    update_value_lin_t update_hills(hillw,0);
    for(unsigned i=0; i!=state.enemyHills.size(); ++i)
      safe_walk(state.grid(),state.enemyHills[i],state.viewradius*100,update_hills);

#if 0
    // boost safe locations
    for(unsigned row=0;row!=(unsigned)(state.rows); ++row)
      for(unsigned col=0;col!=(unsigned)(state.cols); ++col)
        if(state.grid().at(row,col).isSafe) state.grid().at(row,col).value += 1.0;
#endif
}


/// Find N ants with shortest valid route to a given place
struct find_closest_ants_walking_t
{
    unsigned int nb_ants_max;
    int team;
    std::vector<bfs_node> closest_ants;
    find_closest_ants_walking_t(unsigned int _nb_ants, int _team) :  nb_ants_max(_nb_ants), team(_team) {}

    bool operator()(Map & map, const bfs_node & node)
    {
        uLocation cLoc = node.cell;
        int direction = node.direction;
        unsigned depth = node.depth;

        if (closest_ants.size() >= nb_ants_max) return false;
        if(map.at(cLoc).ant == team)
        {
            int d = (direction+2)%4; // opposite direction since we were looking from food to ant
            closest_ants.push_back(bfs_node(cLoc, d, depth));
            return (closest_ants.size() < nb_ants_max);
        }
        return true;
    }
};


void assign_to(State & state, Order & order, uLocation sLoc, unsigned pop = 2, double bonus = 1.0)
{
  TRACK();
  
  find_closest_ants_walking_t finder(2, 0);
  walk(state.grid(),sLoc,state.viewradius*2,finder);

  for(std::vector<bfs_node>::const_iterator it=finder.closest_ants.begin();
      it!=finder.closest_ants.end(); ++it)
    {
      bug << "Ant at " << state.grid().get_row(it->cell)
	  << "," << state.grid().get_col(it->cell) << std::endl;
      
      uLocation to = state.grid().getLocation(it->cell,it->direction);
      double val=state.grid().at(it->cell).value + bonus + 1.0/(1.0 + it->depth);

      bug << "Direction " << state.grid().get_row(to)
	  << "," << state.grid().get_col(to) << " #score " << val << std::endl;
      
      order.proposeMove(state, it->cell, to, it->direction, val);
    }
}



void assign_to_plan(action_plan_t & plan, State & state, uLocation target, unsigned pop = 2, double bonus = 1.0)
{
  TRACK();
  
  find_closest_ants_walking_t finder(pop, 0);
  walk(state.grid(),target,state.viewradius*10,finder);

  for(std::vector<bfs_node>::const_iterator it=finder.closest_ants.begin();
      it!=finder.closest_ants.end(); ++it)
    {
      bug << "Ant at " << state.grid().get_row(it->cell)
	  << "," << state.grid().get_col(it->cell) << std::endl;
      
      uLocation to = state.grid().getLocation(it->cell,it->direction);
      double val = state.grid().at(it->cell).value + bonus + 1.0/(1.0 + it->depth);

      bug << "Direction " << state.grid().get_row(to)
	  << "," << state.grid().get_col(to) << " #score " << val << std::endl;
      
      plan.proposeTarget(it->cell, target, it->depth, it->direction, val);
    }
}






void assign_to_food(State & state, Order & order)
{
    TRACK();

    action_plan_t food_plan;


    for(unsigned i=0; i!=state.food.size(); ++i)
    {
        bug << "Food at " << state.grid().get_row(state.food[i])
            << "," << state.grid().get_col(state.food[i]) << std::endl;

	assign_to_plan(food_plan, state, state.food[i], 10, foodw);
    }

    food_plan.execute_plan(state, order);
}



/// searches first risky cell around and stores it as target
struct alert_t
{
  unsigned dist;
  signed myteam;
  bfs_node target;

  alert_t(unsigned _maxdist, signed _myteam = 0) : 
    dist(_maxdist+1), 
    myteam(_myteam),
    target(0,0,0)
  {}

  bool operator()(Map & map, const bfs_node & node)
  {
        uLocation cLoc = node.cell;

        if((!map[cLoc].isVisible) or 
	   ((map[cLoc].ant != -1) and (map[cLoc].ant != myteam)))
	  {
	    target = node;
	    dist = node.depth;
	    return false;
	  }
	return true;
  }
};


//TODO
void assign_to_guard(State & state, Order & order)
{
    TRACK();

    for(std::vector<uLocation>::const_iterator it_hill=state.myHills.begin();
        it_hill!=state.myHills.end(); ++it_hill)
    {
      bug << "My hill at " << state.grid().get_row(*it_hill)
	  << "," << state.grid().get_col(*it_hill) << std::endl;
      
      alert_t alert(state.viewradius,0);
      walk(state.grid(), *it_hill, state.viewradius, alert);

      bool enemy_around = alert.dist<=state.viewradius;
      
      if(enemy_around)
	{

	  if(state.grid()[alert.target.cell].ant >= 0)
	    {
	      bug << "Menaced by: " << state.grid().get_row(alert.target.cell)
		  << "," << state.grid().get_col(alert.target.cell) << std::endl;
	      assign_to(state, order, alert.target.cell, 10, 2*hillw);
	    }
	  else
	    bug << "Worried by: " << state.grid().get_row(alert.target.cell)
		<< "," << state.grid().get_col(alert.target.cell) << std::endl;
	  
	}
      else
	{
	  // repulses ants
	  update_value_lin_t update_my_hill(-1.0,0);
	  walk(state.grid(),*it_hill,state.viewradius+state.turn,update_my_hill);
	}

      if(state.myAnts.size()> 15 and state.hive < 5 and not enemy_around)
	{
	  bug << "Close the door to save food" << std::endl;
	  state.grid()[*it_hill].value += 2.0;
	}
      else 
	{
	  bug << "Open the door" << std::endl;
	  state.grid()[*it_hill].value -= 1.0;
	}
    }

}

void assign_to_raze_hills(State & state, Order & order)
{
    TRACK();

    for(std::vector<uLocation>::const_iterator it_hill=state.enemyHills.begin();
        it_hill!=state.enemyHills.end(); ++it_hill)
    {
        bug << "Enemy hill at " << state.grid().get_row(*it_hill)
            << "," << state.grid().get_col(*it_hill) << std::endl;

	assign_to(state, order, *it_hill, 10, hillw);
    }
}



